<?php

// https://github.com/picocms/Pico/issues/394#issuecomment-330030163
// unicode arrow: 🡵 or 🡥 or ↗ (the last one seems to be the most common)
// CSS:
// a.external::after { line-height: 100%; vertical-align: super; font-size: 60%; content: "↗" }
// and if you don't want the arrow on some external links, add the "noarrow" class. CSS:
// a.external.noarrow::after { content: '' }

class PicoTargetBlank extends AbstractPicoPlugin
{
    /**
     * This plugin is enabled by default
    * but you can still disable it in config.yml:
    * 
    * PicoTargetBlank:
    *     enabled: false
    * 
    * You can also set the rel= attribute, or disable it with an empty string:
    *     rel: 'noopener noreferrer nofollow
    * or
    *     rel: ''
    * etc.
    * 
    * defaults to 'noopener noreferrer' if unspecified.
    * Also see:
    * pointjupiter.com/what-noopener-noreferrer-nofollow-explained/
     */
    const API_VERSION = 3;

    protected $doit = true;

    public function onConfigLoaded(&$config)
    {
        $this->rel = $this->getPluginConfig('rel', 'noopener noreferrer');
        $this->exclude = $this->getPluginConfig('exclude',null);
    }

    public function onRequestUrl(&$url)
    {
        $name = $url == "" ? "index" : $url;
        if(in_array($url,$this->exclude)) $this->doit=false;
    }

    public function onContentParsed(&$content)
    {

        if(trim($content)=="") return;
        if($this->doit === false) return;

        $dom = new DOMDocument();
        $dom->preserveWhiteSpace = true;
        // encoding problems require us to jump through hoops here...
        //~ $dom->loadXML($utf8.$content,LIBXML_NOWARNING|LIBXML_NONET|LIBXML_COMPACT|LIBXML_NOERROR);
        // $content is a html fragment. We like to modify its content with DOMDocument, but it prefers complete documents and completes them
        // if necessary. This leads to strangeness. The best thing I found is to give it a complete document with all required headers and tags,
        // then strip those off in the end. That way DOMDocument doesn't meddle, and we have control. Hah, hopefully.
        $dom->loadHTML('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$content.'</body></html>',
        LIBXML_NONET|LIBXML_COMPACT|LIBXML_HTML_NODEFDTD|LIBXML_NOWARNING);
        //~ LIBXML_NOEMPTYTAG|LIBXML_NOENT|LIBXML_HTML_NOIMPLIED|LIBXML_NOERROR);
        $trim_before=92; // What we need to trim when saving (the above string added to $content)
        $trim_after=-15; //   - " -
        // please read this: stackoverflow.com/a/11310258
        //~ $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));

        // This we need only once
        // remove port from HTTP_HOST, if applicable
        $host = preg_replace('/:[0-9]+$/','',$_SERVER['HTTP_HOST']);

        // add target _blank to external links
        $links = $dom->getElementsByTagName('a');
        //~ var_dump($links);
        //~ array_walk(iterator_to_array($links),'var_dump');
        //~ die();
            //~ echo "Before ";
        foreach ($links as $item) {
            $href = parse_url($item->getAttribute('href'), PHP_URL_HOST);
            //~ echo "foreach ".$href.' ';

            // IF THIS LINK IS EXTERNAL (differs from the server)...
            if (!empty($href) && $href != $host){
            // open in new tab
                $item->setAttribute('target','_blank');
            // set rel attribute (configurable)
                if (!empty($this->rel)) $item->setAttribute('rel',$this->rel);
            // if it has a class already, preserve it
            if ( $item->hasAttribute('class') ) {
               $string = $item->getAttribute('class') . ' external';
            }
            else $string = 'external';
            // set class to $string
            $item->setAttribute('class',$string);

            // if it has a title already, preserve it
            if ( $item->hasAttribute('title') ) {
               $string = $item->getAttribute('title') . ' - external link: ' . $href;
            }
            else $string = 'External: ' . $href;
            // set title to $string
                $item->setAttribute('title',$string);
            }

        }
        //~ // we now have a full html document in $dom. Let's extract only what is between
        //~ // <body> tags,as it was before. - stackoverflow.com/a/18090774
        //~ $content='';
        //~ foreach($dom->getElementsByTagName("body")->item(0)->childNodes as $child) {
            //~ $content .= $dom->saveXML($child);
        //~ }
        $content = $dom->saveHTML();
        // remove the tag we added at loadHTML
        $content = substr($content,$trim_before,$trim_after);
        // please read this: stackoverflow.com/a/20675396
        //~ $content = $dom->saveHTML($dom->documentElement);
    }
}
